package com.bestvike.stone.spring.shiro.mgt;

import com.bestvike.linq.Linq;
import com.bestvike.stone.spring.context.ThreadContext;
import com.bestvike.stone.spring.shiro.annotation.resolver.AnnotationResolver;
import com.bestvike.stone.spring.shiro.auth.Auth;
import com.bestvike.stone.spring.shiro.authc.AuthenticationToken;
import com.bestvike.stone.spring.shiro.authc.exception.AuthenticationException;
import com.bestvike.stone.spring.shiro.authc.exception.token.UnsupportedTokenException;
import com.bestvike.stone.spring.shiro.cache.CacheManager;
import com.bestvike.stone.spring.shiro.realm.Realm;
import org.springframework.util.Assert;

import java.util.Arrays;
import java.util.Collection;
import java.util.Set;

/**
 * 默认安全管理器
 */
public final class DefaultSecurityManager implements SecurityManager {
    private AnnotationResolver annotationResolver;
    private CacheManager cacheManager;
    private Collection<Realm> realms;


    //region properties

    /**
     * 获取注解解析器
     */
    @Override
    public AnnotationResolver getAnnotationResolver() {
        Assert.notNull(this.annotationResolver, "must set annotationResolver");
        return this.annotationResolver;
    }

    /**
     * 设置注解解析器
     */
    public void setAnnotationResolver(AnnotationResolver annotationResolver) {
        this.annotationResolver = annotationResolver;
    }

    /**
     * 获取缓存管理器
     */
    @Override
    public CacheManager getCacheManager() {
        Assert.notNull(this.cacheManager, "must set cacheManager");
        return this.cacheManager;
    }

    /**
     * 设置缓存管理器
     */
    public void setCacheManager(CacheManager cacheManager) {
        this.cacheManager = cacheManager;
    }

    /**
     * 获取认证领域集合
     */
    @Override
    public Collection<Realm> getRealms() {
        return this.realms;
    }

    /**
     * 设置认证领域集合
     */
    public void setRealms(Collection<Realm> realms) {
        this.realms = realms;
    }

    //endregion


    //region authc

    /**
     * 登录
     *
     * @param token 登录信息
     * @return 认证结果
     * @throws AuthenticationException 登录失败异常
     */
    @Override
    public Auth login(AuthenticationToken token) throws AuthenticationException {
        final Auth auth = this.authenticate(token);
        ThreadContext.setAuth(auth);
        this.getCacheManager().save(auth);
        return auth;
    }

    /**
     * 登出
     *
     * @param token 令牌
     */
    @Override
    public void logout(String token) {
        ThreadContext.setAuth(null);
        this.getCacheManager().delete(token);
    }

    /**
     * 认证
     */
    private Auth authenticate(AuthenticationToken token) throws AuthenticationException {
        final Collection<Realm> realms = this.getRealms();
        if (realms == null)
            throw new UnsupportedTokenException();
        for (Realm realm : realms) {
            if (realm.supports(token))
                return realm.authenticate(token);
        }
        throw new UnsupportedTokenException();
    }

    //endregion


    //region authz

    /**
     * 根据上下文令牌从缓存取回授权
     */
    @Override
    public Auth fetch() {
        Auth auth = ThreadContext.getAuth();
        if (auth == null) {
            if (this.cacheManager != null) {
                auth = this.cacheManager.fetch(ThreadContext.getToken());
                ThreadContext.setAuth(auth);
            }
        }
        return auth;
    }

    /**
     * 每次授权验证时,再次校验令牌有效性
     */
    @Override
    public boolean isAuthenticated() {
        final Auth auth = this.fetch();
        return auth != null && auth.isAuthenticated();
    }

    /**
     * 判断当前上下文是否有指定的全部权限
     *
     * @param permissions 指定的权限
     * @return 有返回 true, 否则返回 false
     */
    @Override
    public boolean hasAllPermission(String... permissions) {
        Assert.notNull(permissions, "permissions cant not be null");
        final Auth auth = this.fetch();
        if (auth == null || !auth.isAuthenticated())
            return false;
        final Set<String> authPermissions = auth.getPermissions();
        return authPermissions != null && authPermissions.containsAll(Arrays.asList(permissions));
    }

    /**
     * 判断当前上下文是否有指定的任一权限
     *
     * @param permissions 指定的权限
     * @return 有返回 true, 否则返回 false
     */
    @Override
    public boolean hasAnyPermission(String... permissions) {
        Assert.notNull(permissions, "permissions cant not be null");
        final Auth auth = this.fetch();
        if (auth == null || !auth.isAuthenticated())
            return false;
        final Set<String> authPermissions = auth.getPermissions();
        return authPermissions != null && Linq.asEnumerable(permissions).any(authPermissions::contains);
    }

    /**
     * 判断当前上下文是否有指定的全部角色
     *
     * @param roles 指定的角色
     * @return 有返回 true, 否则返回 false
     */
    @Override
    public boolean hasAllRole(String... roles) {
        Assert.notNull(roles, "roles cant not be null");
        final Auth auth = this.fetch();
        if (auth == null || !auth.isAuthenticated())
            return false;
        final Set<String> authRoles = auth.getRoles();
        return authRoles != null && authRoles.containsAll(Arrays.asList(roles));
    }

    /**
     * 判断当前上下文是否有指定的任一角色
     *
     * @param roles 指定的角色
     * @return 有返回 true, 否则返回 false
     */
    @Override
    public boolean hasAnyRole(String... roles) {
        Assert.notNull(roles, "roles cant not be null");
        final Auth auth = this.fetch();
        if (auth == null || !auth.isAuthenticated())
            return false;
        final Set<String> authRoles = auth.getRoles();
        return authRoles != null && Linq.asEnumerable(roles).any(authRoles::contains);
    }

    //endregion
}
